package com.hanserwei.community.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hanserwei.community.entity.po.Menu;
import com.hanserwei.community.entity.request.menu.MenuAddRequest;
import com.hanserwei.community.entity.request.menu.MenuUpdateRequest;
import com.hanserwei.community.entity.vo.MenuVO;
import com.hanserwei.community.enums.ResponseCodeEnum;
import com.hanserwei.community.exception.ApiException;
import com.hanserwei.community.mapper.MenuMapper;
import com.hanserwei.community.service.MenuService;
import lombok.RequiredArgsConstructor;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements MenuService {
    private final MenuMapper menuMapper;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateRoleMenu(List<MenuVO> menuVOList, Integer roleId) {
        //删除角色菜单关系
        menuMapper.deleteRoleMenu(roleId);
        //添加角色菜单关系
        List<Integer> menuIds = menuVOList.stream().map(MenuVO::getId).toList();
        menuMapper.addRoleMenu(roleId, menuIds);
        return true;
    }

    @Override
    public List<MenuVO> queryMenuByRole(String role) {
        List<Menu> menuList = menuMapper.queryMenuByRole(role);
        return getMenuVOList(menuList);
    }

    @Override
    public List<MenuVO> queryMenuByUserName(String userName) {
        // 1. 从数据库查询扁平化的菜单列表
        List<Menu> menuList = menuMapper.queryMenuByUserName(userName);

        return getMenuVOList(menuList);
    }

    private @NotNull List<MenuVO> getMenuVOList(List<Menu> menuList) {
        // 2. 将 List<Menu> 转换为 List<MenuVO>
        List<MenuVO> menuVOList = menuList.stream()
                .map(menu -> {
                    MenuVO menuVO = new MenuVO();
                    BeanUtils.copyProperties(menu, menuVO);
                    return menuVO;
                })
                .toList();

        // 3. 将扁平化的 List<MenuVO> 转换为树形结构
        // 使用 groupingBy 按 pid 分组
        Map<Integer, List<MenuVO>> menuGroups = menuVOList.stream()
                .collect(Collectors.groupingBy(MenuVO::getPid));

        List<MenuVO> rootMenus = menuGroups.getOrDefault(0, new ArrayList<>());

        // 4. 遍历根菜单，为每个根菜单递归设置子菜单
        rootMenus.forEach(root -> {
            // 使用一个内部递归方法来构建子树
            setChildren(root, menuGroups);
        });
        return rootMenus;
    }

    /**
     * 递归方法：为父菜单设置子菜单
     */
    private void setChildren(MenuVO parent, Map<Integer, List<MenuVO>> menuGroups) {
        List<MenuVO> children = menuGroups.get(parent.getId());
        if (children != null) {
            parent.setChildren(children);
            children.forEach(child -> setChildren(child, menuGroups));
        }
    }

    @Override
    public List<MenuVO> getMenuTree() {
        // 查询所有菜单，按排序号排序
        LambdaQueryWrapper<Menu> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.orderByAsc(Menu::getPid, Menu::getSortId);
        List<Menu> allMenus = list(queryWrapper);
        return getMenuVOList(allMenus);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean addMenu(MenuAddRequest request) {
        // 检查父菜单是否存在（如果不是顶级菜单）
        if (request.getPid() != 0) {
            Menu parentMenu = getById(request.getPid());
            if (parentMenu == null) {
                throw new ApiException(ResponseCodeEnum.INVALID_PARAMETER, "父级菜单不存在");
            }
            // 检查菜单层级（最多支持二级菜单）
            if (parentMenu.getPid() != 0) {
                throw new ApiException(ResponseCodeEnum.INVALID_PARAMETER, "最多支持二级菜单，不能在二级菜单下添加子菜单");
            }
        }

        // 检查同级菜单名称是否重复
        LambdaQueryWrapper<Menu> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Menu::getPid, request.getPid())
                   .eq(Menu::getName, request.getName());
        if (count(queryWrapper) > 0) {
            throw new ApiException(ResponseCodeEnum.INVALID_PARAMETER, "同级菜单名称不能重复");
        }

        // 创建菜单对象
        Menu menu = new Menu();
        BeanUtils.copyProperties(request, menu);
        menu.setCreateTime(LocalDateTime.now());
        menu.setUpdateTime(LocalDateTime.now());
        
        // 如果没有设置排序号，自动设置为同级菜单的最大排序号+1
        if (request.getSortId() == null) {
            LambdaQueryWrapper<Menu> sortWrapper = new LambdaQueryWrapper<>();
            sortWrapper.eq(Menu::getPid, request.getPid())
                      .orderByDesc(Menu::getSortId)
                      .last("LIMIT 1");
            Menu lastMenu = getOne(sortWrapper);
            menu.setSortId(lastMenu != null ? lastMenu.getSortId() + 1 : 1);
        }

        return save(menu);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateMenu(MenuUpdateRequest request) {
        // 检查菜单是否存在
        Menu existingMenu = getById(request.getId());
        if (existingMenu == null) {
            throw new ApiException(ResponseCodeEnum.RESOURCE_NOT_FOUND, "菜单不存在");
        }

        // 检查父菜单是否存在（如果不是顶级菜单）
        if (request.getPid() != 0) {
            Menu parentMenu = getById(request.getPid());
            if (parentMenu == null) {
                throw new ApiException(ResponseCodeEnum.INVALID_PARAMETER, "父级菜单不存在");
            }
            // 不能将菜单设置为自己的子菜单
            if (request.getPid().equals(request.getId())) {
                throw new ApiException(ResponseCodeEnum.INVALID_PARAMETER, "不能将菜单设置为自己的子菜单");
            }
            // 检查菜单层级（最多支持二级菜单）
            if (parentMenu.getPid() != 0) {
                throw new ApiException(ResponseCodeEnum.INVALID_PARAMETER, "最多支持二级菜单，不能在二级菜单下添加子菜单");
            }
        }

        // 如果修改了父级菜单，需要检查是否会形成循环引用
        if (!request.getPid().equals(existingMenu.getPid())) {
            // 检查是否有子菜单，如果有子菜单则不能修改为非顶级菜单
            LambdaQueryWrapper<Menu> childWrapper = new LambdaQueryWrapper<>();
            childWrapper.eq(Menu::getPid, request.getId());
            if (count(childWrapper) > 0 && request.getPid() != 0) {
                throw new ApiException(ResponseCodeEnum.INVALID_PARAMETER, "该菜单下有子菜单，不能修改为非顶级菜单");
            }
        }

        // 检查同级菜单名称是否重复（排除自己）
        LambdaQueryWrapper<Menu> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Menu::getPid, request.getPid())
                   .eq(Menu::getName, request.getName())
                   .ne(Menu::getId, request.getId());
        if (count(queryWrapper) > 0) {
            throw new ApiException(ResponseCodeEnum.INVALID_PARAMETER, "同级菜单名称不能重复");
        }

        // 更新菜单
        Menu menu = new Menu();
        BeanUtils.copyProperties(request, menu);
        menu.setUpdateTime(LocalDateTime.now());
        return updateById(menu);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteMenu(Integer id) {
        // 检查菜单是否存在
        Menu menu = getById(id);
        if (menu == null) {
            throw new ApiException(ResponseCodeEnum.RESOURCE_NOT_FOUND, "菜单不存在");
        }

        // 检查是否有子菜单
        LambdaQueryWrapper<Menu> childWrapper = new LambdaQueryWrapper<>();
        childWrapper.eq(Menu::getPid, id);
        if (count(childWrapper) > 0) {
            throw new ApiException(ResponseCodeEnum.INVALID_PARAMETER, "该菜单下有子菜单，请先删除子菜单");
        }

        // 检查是否有角色关联
        boolean hasRoleAssociation = menuMapper.checkMenuRoleAssociation(id);
        if (hasRoleAssociation) {
            throw new ApiException(ResponseCodeEnum.INVALID_PARAMETER, "该菜单已被角色关联，无法删除");
        }

        return removeById(id);
    }

    @Override
    public MenuVO getMenuById(Integer id) {
        Menu menu = getById(id);
        if (menu == null) {
            throw new ApiException(ResponseCodeEnum.RESOURCE_NOT_FOUND, "菜单不存在");
        }
        
        MenuVO menuVO = new MenuVO();
        BeanUtils.copyProperties(menu, menuVO);
        return menuVO;
    }
}
